package com.rickpan.storage.url;

import com.rickpan.config.MinIOProperties;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.http.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * MinIO URL 生成服务
 * 提供高性能的预签名 URL 生成功能
 */
@Service
@ConditionalOnProperty(name = "app.storage.type", havingValue = "minio")
public class MinIOUrlGenerationService {
    
    private static final Logger logger = LoggerFactory.getLogger(MinIOUrlGenerationService.class);
    
    @Autowired
    private MinioClient minioClient;
    
    @Autowired
    private MinIOProperties minIOProperties;
    
    // URL 缓存，避免重复生成相同的 URL
    private final Map<String, CachedUrl> urlCache = new ConcurrentHashMap<>();
    
    // 缓存清理间隔（毫秒）
    private static final long CACHE_CLEANUP_INTERVAL = 5 * 60 * 1000L; // 5分钟
    private long lastCleanupTime = System.currentTimeMillis();
    
    /**
     * 缓存的 URL 信息
     */
    private static class CachedUrl {
        private final String url;
        private final long expirationTime;
        private final long createdTime;
        
        public CachedUrl(String url, long expirationTime) {
            this.url = url;
            this.expirationTime = expirationTime;
            this.createdTime = System.currentTimeMillis();
        }
        
        public boolean isExpired() {
            return System.currentTimeMillis() >= expirationTime;
        }
        
        public boolean isNearExpiry(long bufferSeconds) {
            return System.currentTimeMillis() >= (expirationTime - bufferSeconds * 1000);
        }
        
        public String getUrl() { return url; }
        public long getExpirationTime() { return expirationTime; }
        public long getCreatedTime() { return createdTime; }
    }
    
    /**
     * 生成下载 URL
     *
     * @param objectKey 对象键
     * @param expiration URL 有效期
     * @return 预签名下载 URL
     */
    public String generateDownloadUrl(String objectKey, Duration expiration) {
        return generatePresignedUrl(objectKey, Method.GET, expiration, "download", null);
    }

    /**
     * 生成下载 URL（支持自定义Content-Disposition）
     *
     * @param objectKey 对象键
     * @param expiration URL 有效期
     * @param forceDownload 是否强制下载（true=attachment, false=inline）
     * @param filename 文件名
     * @return 预签名下载 URL
     */
    public String generateDownloadUrl(String objectKey, Duration expiration, boolean forceDownload, String filename) {
        String disposition = forceDownload ? "attachment" : "inline";
        if (filename != null) {
            try {
                String encodedFilename = java.net.URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
                disposition += "; filename*=UTF-8''" + encodedFilename;
            } catch (Exception e) {
                logger.warn("文件名编码失败: {}", filename, e);
            }
        }
        return generatePresignedUrl(objectKey, Method.GET, expiration, "download", disposition);
    }
    
    /**
     * 生成预览 URL
     * 
     * @param objectKey 对象键
     * @param expiration URL 有效期
     * @return 预签名预览 URL
     */
    public String generatePreviewUrl(String objectKey, Duration expiration) {
        return generatePresignedUrl(objectKey, Method.GET, expiration, "preview");
    }
    
    /**
     * 批量生成下载 URL
     * 
     * @param objectKeys 对象键列表
     * @param expiration URL 有效期
     * @return 对象键到 URL 的映射
     */
    public Map<String, String> batchGenerateDownloadUrls(List<String> objectKeys, Duration expiration) {
        logger.debug("批量生成下载URL: {} 个文件", objectKeys.size());
        
        return objectKeys.parallelStream()
            .collect(Collectors.toConcurrentMap(
                objectKey -> objectKey,
                objectKey -> generateDownloadUrl(objectKey, expiration)
            ));
    }
    
    /**
     * 批量生成预览 URL
     * 
     * @param objectKeys 对象键列表
     * @param expiration URL 有效期
     * @return 对象键到 URL 的映射
     */
    public Map<String, String> batchGeneratePreviewUrls(List<String> objectKeys, Duration expiration) {
        logger.debug("批量生成预览URL: {} 个文件", objectKeys.size());
        
        return objectKeys.parallelStream()
            .collect(Collectors.toConcurrentMap(
                objectKey -> objectKey,
                objectKey -> generatePreviewUrl(objectKey, expiration)
            ));
    }
    
    /**
     * 刷新 URL（如果即将过期）
     * 
     * @param objectKey 对象键
     * @param urlType URL 类型（download/preview）
     * @param expiration 新的有效期
     * @return 刷新后的 URL
     */
    public String refreshUrlIfNeeded(String objectKey, String urlType, Duration expiration) {
        String cacheKey = buildCacheKey(objectKey, urlType);
        CachedUrl cachedUrl = urlCache.get(cacheKey);
        
        if (cachedUrl == null || cachedUrl.isNearExpiry(300)) { // 5分钟缓冲期
            logger.debug("URL即将过期，刷新URL: {}", objectKey);
            
            if ("download".equals(urlType)) {
                return generateDownloadUrl(objectKey, expiration);
            } else {
                return generatePreviewUrl(objectKey, expiration);
            }
        }
        
        return cachedUrl.getUrl();
    }
    
    /**
     * 生成预签名 URL（核心方法）
     *
     * @param objectKey 对象键
     * @param method HTTP 方法
     * @param expiration URL 有效期
     * @param urlType URL 类型（用于缓存）
     * @return 预签名 URL
     */
    private String generatePresignedUrl(String objectKey, Method method, Duration expiration, String urlType) {
        return generatePresignedUrl(objectKey, method, expiration, urlType, null);
    }

    /**
     * 生成预签名 URL（支持自定义响应头）
     *
     * @param objectKey 对象键
     * @param method HTTP 方法
     * @param expiration URL 有效期
     * @param urlType URL 类型（用于缓存）
     * @param contentDisposition 自定义Content-Disposition
     * @return 预签名 URL
     */
    private String generatePresignedUrl(String objectKey, Method method, Duration expiration, String urlType, String contentDisposition) {
        // 清理过期缓存
        cleanupExpiredCache();
        
        // 检查缓存
        String cacheKey = buildCacheKey(objectKey, urlType);
        CachedUrl cachedUrl = urlCache.get(cacheKey);
        
        if (cachedUrl != null && !cachedUrl.isExpired() && !cachedUrl.isNearExpiry(60)) {
            logger.debug("使用缓存的URL: {}", objectKey);
            return cachedUrl.getUrl();
        }
        
        try {
            long startTime = System.currentTimeMillis();
            
            // 构建预签名URL参数
            GetPresignedObjectUrlArgs.Builder argsBuilder = GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(minIOProperties.getBucketName())
                    .object(objectKey)
                    .expiry((int) expiration.getSeconds(), TimeUnit.SECONDS);

            // 如果指定了Content-Disposition，添加response-content-disposition参数
            if (contentDisposition != null) {
                java.util.Map<String, String> extraQueryParams = new java.util.HashMap<>();
                extraQueryParams.put("response-content-disposition", contentDisposition);
                argsBuilder.extraQueryParams(extraQueryParams);
            }

            // 生成预签名 URL
            String url = minioClient.getPresignedObjectUrl(argsBuilder.build());
            
            // 缓存 URL
            long expirationTime = System.currentTimeMillis() + expiration.toMillis();
            urlCache.put(cacheKey, new CachedUrl(url, expirationTime));
            
            long duration = System.currentTimeMillis() - startTime;
            logger.debug("MinIO生成{}URL: {} -> {} (耗时: {}ms)", urlType, objectKey, url, duration);
            
            return url;
            
        } catch (Exception e) {
            logger.error("MinIO生成{}URL失败: {}", urlType, objectKey, e);
            throw new RuntimeException("MinIO生成" + urlType + "URL失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 构建缓存键
     * 
     * @param objectKey 对象键
     * @param urlType URL 类型
     * @return 缓存键
     */
    private String buildCacheKey(String objectKey, String urlType) {
        return urlType + ":" + objectKey;
    }
    
    /**
     * 清理过期的缓存
     */
    private void cleanupExpiredCache() {
        long currentTime = System.currentTimeMillis();
        
        // 每5分钟清理一次
        if (currentTime - lastCleanupTime < CACHE_CLEANUP_INTERVAL) {
            return;
        }
        
        int beforeSize = urlCache.size();
        urlCache.entrySet().removeIf(entry -> entry.getValue().isExpired());
        int afterSize = urlCache.size();
        
        if (beforeSize > afterSize) {
            logger.debug("清理过期URL缓存: {} -> {} (清理了{}个)", beforeSize, afterSize, beforeSize - afterSize);
        }
        
        lastCleanupTime = currentTime;
    }
    
    /**
     * 获取缓存统计信息
     * 
     * @return 缓存统计
     */
    public CacheStats getCacheStats() {
        cleanupExpiredCache();
        
        int totalCached = urlCache.size();
        int expiredCount = (int) urlCache.values().stream()
            .mapToLong(url -> url.isExpired() ? 1 : 0)
            .sum();
        
        return new CacheStats(totalCached, expiredCount);
    }
    
    /**
     * 清空所有缓存
     */
    public void clearCache() {
        int size = urlCache.size();
        urlCache.clear();
        logger.info("清空URL缓存: {} 个", size);
    }
    
    /**
     * 缓存统计信息
     */
    public static class CacheStats {
        private final int totalCached;
        private final int expiredCount;
        
        public CacheStats(int totalCached, int expiredCount) {
            this.totalCached = totalCached;
            this.expiredCount = expiredCount;
        }
        
        public int getTotalCached() { return totalCached; }
        public int getExpiredCount() { return expiredCount; }
        public int getValidCount() { return totalCached - expiredCount; }
    }
    
    /**
     * 获取默认下载 URL 有效期
     */
    public Duration getDefaultDownloadExpiration() {
        return Duration.ofSeconds(minIOProperties.getDefaultUrlExpiration());
    }
    
    /**
     * 获取默认预览 URL 有效期
     */
    public Duration getDefaultPreviewExpiration() {
        return Duration.ofSeconds(minIOProperties.getPreviewUrlExpiration());
    }
}
